/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package business;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * Histograma 1D
 *
 * @author jonas
 */
public class Histograma {

    private float larguraPanorama; //Utilizado na regra de 3 para converter ponto em grau
    private int[] conteudo;//Conteúdo do histograma (todos os valores)
    private float[] pontos;
    private int numAgrupador = 10; //Representa o valor para agrupar os graus. Ex.: 10 em 10 graus
    private int tamanhoJanela = 3; //Representa a quantidade de angulos utilizados para calcular o maximo 
    private double grauComMaisMatches = -1;
    private String panoId;
    private int maiorNumeroMatchesMedia;
    private double norte;
    private int numeroTotalMatches;
    
    
    Histograma(String panoId , float larguraP, double norte){
        this.panoId = panoId;
        this.larguraPanorama = (larguraP / 4);
        this.norte = norte;
        conteudo = new int[360 / numAgrupador];
    }

    //Metodo que preenche o histograma com conteúdos
    public int criarHistograma() throws FileNotFoundException, IOException {
        lerPontos();
        for (float ponto : pontos) {
            //incrementa no array, o número de pontos que cairam naquele grau
            conteudo[converterEmGraus(ponto) / numAgrupador] = conteudo[converterEmGraus(ponto)/ numAgrupador] + 1;
        }

        //seta para a variavel, a maior media de matches
        maiorNumeroMatchesMedia = calculaMaximo();

        //salva histograma
        salvaHistograma();
        
        return maiorNumeroMatchesMedia;
    }

    //Regra de 3 para converter um ponto do eixo x, em grau
    private int converterEmGraus(float x) {
        //Caulca o grau o convertendo no eixo x (entre -1 e 1) e somando a rotacao (se necessario)
        int grau = (int) Math.toDegrees(Math.atan(converteOEspaco(x))) + (verificaLado(x) * 90);

        //AGRUPA ENTRE VALORES
        int grauAgrupado = (grau / numAgrupador) * numAgrupador;
        
        //Verifica se o numero e negativo, caso seja, utiliza o complementar a 360 pos se trata de um circulo
        return (grauAgrupado >= 0 ? grauAgrupado : 360 + grauAgrupado);
    }

    
    /*Este Metodo converte do espaco 0 a width para o espaco -1 a 1 utilizando interpolacao
     * alem disso, cria a rotacao, se necessario */
    private float converteOEspaco(float x) {
        return ((2 * (x - (larguraPanorama * verificaLado(x))) ) - larguraPanorama) / larguraPanorama;
    }
    
    /*Este Metodo verifica quantos graus sao necessarios somar ao resultado final
     * dependendo do lado que estamos calculando
     */
    private int verificaLado(float x) {
        int posicaoLado;
        
        if(x < larguraPanorama){
            posicaoLado = 0;
        } else {
            if( (x > larguraPanorama) && (x < (2 * larguraPanorama)) ){
                posicaoLado = 1;
            } else {
                if( (x > (2 * larguraPanorama)) && (x < (3 * larguraPanorama)) ){
                    posicaoLado = 2;
                } else {
                    posicaoLado = 3;
                }
            }
        }
        
        return posicaoLado;
    }

    /*Este Metodo deve calcular o maximo valor medio em uma janela.
     * Exemplo: O maximo valor de um histograma pode ser a media entre os graus 10, 20 e 30, utilizando
     * uma janela de tamanho 3 e numero agrupador de 10 em 10 graus.
     */
    private int calculaMaximo() {
        int maiorMedia = 0;
//        for (int i = 0; i < (conteudo.length - tamanhoJanela); i++) {
        
        //TODO colocar nome melhor
        //Essa variavel controla o numero de posicoes do array que devem ser
        //"colocadas" ao final do array de conteudos para que se possa realizar uma
        //comparacao ciclica. Ou seja, para que volte a comparar os ultimos com os primeiros
        int numPosicoesAddNoFinal = tamanhoJanela - 1;
        
       for (int i = 0; i < conteudo.length; i++) {
            int soma = 0;

            if (i < conteudo.length - (numPosicoesAddNoFinal)) {
                for (int k = 0; k < tamanhoJanela; k++) {
                    soma = soma + conteudo[i + k];
                }
            } else {

                for (int b = i; b < conteudo.length; b++) {
                    soma = soma + conteudo[b];
                }

                int x = (tamanhoJanela - ((conteudo.length - 1) - i)) - 2;
                for (int j = x; j >= 0; j--) {
                    soma = soma + conteudo[j];
                }

            }

            int media = soma / tamanhoJanela;
            
//            maiorMedia = media > maiorMedia ? media : maiorMedia;
            
            if(media > maiorMedia){
                maiorMedia = media;
                
                //seta para a variavel quem foi o grau que gerou a maior media
                grauComMaisMatches = i * numAgrupador;
            }
            
        }

        converteGrauComBaseNorte();
        
        return maiorMedia;
    }
    
    private void converteGrauComBaseNorte(){
        double grauBaseNorte = (360 - norte) + grauComMaisMatches;
        grauComMaisMatches = grauBaseNorte < 360 ? grauBaseNorte : (grauBaseNorte - 360);
    }

    /*Este Metodo le do arquivo de matches referentes ao panorama e inicializa
     * o array de pontos, com os valores encontrados nesse arquivo
     */
    private void lerPontos() throws FileNotFoundException, IOException {
        File arq = new File("temp/" + panoId + "_matches.txt");

        FileReader file = new FileReader(arq);
        BufferedReader buf = new BufferedReader(file);

        numeroTotalMatches = Integer.parseInt(buf.readLine());
        pontos = new float[numeroTotalMatches];

        for (int i = 0; i < numeroTotalMatches; i++) {
            String linha = buf.readLine();
            int index = linha.indexOf(" ");

            String valor = linha.substring(0, index);

            pontos[i] = Float.parseFloat(valor);
        }

    }
    
    private void salvaHistograma() throws FileNotFoundException, IOException {
        File arq = new File("temp/" + panoId + "_histograma.txt");

        FileWriter fw = new FileWriter(arq); 
        BufferedWriter bw = new BufferedWriter(fw);
        
       int grau = 0;
       for (int matche : conteudo) {
            bw.write(grau + ": "+ matche); 
            bw.newLine();  
            grau = grau + 10;
        }
       
       bw.write("Numero Total de Matches: " + numeroTotalMatches);
       bw.newLine();
       bw.write("Norte: " + norte);
       bw.newLine();
       bw.write("A conta para o calculo do angulo com maior numero de matches : "
               + "(360 - Norte) + Angulo mais matches");
       bw.newLine();
       bw.write("360 - Norte = " + (350 - norte));
       bw.newLine();
       bw.write("Angulo maior numero matches (relacao ao norte) : " + grauComMaisMatches);
       bw.close(); 
    }

    public int[] getConteudo() {
        return conteudo;
    }

    public void setConteudo(int[] conteudo) {
        this.conteudo = conteudo;
    }

    public float getLarguraPanorama() {
        return larguraPanorama;
    }

    public int getNumAgrupador() {
        return numAgrupador;
    }

    public void setNumAgrupador(int numAgrupador) {
        this.numAgrupador = numAgrupador;
    }

    public double getGrauComMaisMatches() {
        return grauComMaisMatches;
    }

    public void setGrauComMaisMatches(double grauComMaisMatches) {
        this.grauComMaisMatches = grauComMaisMatches;
    }

    public int getMaiorNumeroMatchesMedia() {
        return maiorNumeroMatchesMedia;
    }

    public void setMaiorNumeroMatchesMedia(int maiorNumeroMatchesMedia) {
        this.maiorNumeroMatchesMedia = maiorNumeroMatchesMedia;
    }
    
}